import datetime
import json
import uuid

from django.db import transaction
from django.db.models import Q
from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

import settings
from social.account.models import User
from social.exceptions import CodeException
from social.feed.models import Post, PostLike
from social.feed.serializers import PostSerializer, PostLikeSerializer
from social.gift.models import Gift
from social.gift.serializers import GiftSerializer
from social.notification.models import NotificationRead
from social.user.models import AVCallSetting, UserLike, UserBlock, UserMsgSetting, UserVisit, UserLevel, UserChatUp, \
    ChatUpCount, UserAuth, GiftWall, NameAuth
from social.user.serializers import UserSerializer, AVCallSettingSerializer, FollowerSerializer, FansSerializer, \
    UserMsgSettingSerializer, VisitorSerializer, UserLevelSerializer, GiftWallSerializer, BlockSerializer
from social.user.tasks import add_user_visit
from social.utils.face_auth import InitFaceVerify, DescribeFaceVerify, CompareFaceVerify
from social.utils.views import BaseGenericViewSet


class UserViewSet(BaseGenericViewSet, ListModelMixin, RetrieveModelMixin):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (IsAuthenticated,)
    pagination_ordering = '-created_at'

    def retrieve(self, request, *args, **kwargs):
        source = request.GET.get('source')
        if source == 'user_info' and request.user.id != self.get_object().id:
            add_user_visit.delay(request.user.id, self.get_object().id)
        return super(UserViewSet, self).retrieve(request, *args, **kwargs)

    def list(self, request, *args, **kwargs):
        im_id = request.GET.get('im_id')
        if im_id:
            self.kwargs['pk'] = im_id[(len(settings.APP_NAME) + 1):]
        else:
            self.kwargs['pk'] = self.request.user.id
        return super(UserViewSet, self).retrieve(request, *args, **kwargs)

    @action(methods=['GET', 'PUT'], detail=False, serializer_class=AVCallSettingSerializer)
    def av_call_setting(self, request, *args, **kwargs):
        settings, _ = AVCallSetting.objects.get_or_create(user=request.user)
        if request.method == 'PUT':
            ser = AVCallSettingSerializer(settings, request.data)
            if ser.is_valid(raise_exception=True):
                user = request.user
                ser.save(user=user)
                from social.search.models import EsUser
                EsUser.update_user(user)
                return Response(data=ser.data)
        else:
            return Response(AVCallSettingSerializer(settings).data)

    @action(methods=['POST'], detail=True)
    def follow(self, request, *args, **kwargs):
        UserLike.follow(request.user, self.get_object())
        return Response()

    @action(methods=['POST'], detail=True)
    def cancel_follow(self, request, *args, **kwargs):
        UserLike.cancel_follow(request.user, self.get_object())
        return Response()

    @action(methods=['GET'], detail=False, serializer_class=FollowerSerializer)
    def followers(self, request, *args, **kwargs):
        self.queryset = UserLike.objects.select_related('to_user').filter(from_user=request.user)
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['GET'], detail=False, serializer_class=FansSerializer)
    def fans(self, request, *args, **kwargs):
        if not request.GET.get('next'):
            read, _ = NotificationRead.objects.get_or_create(user=request.user)
            read.new_fans = int(datetime.datetime.now().timestamp())
            read.save(update_fields=['new_fans'])
        self.queryset = UserLike.objects.select_related('from_user').filter(to_user=request.user)
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['GET'], detail=False, serializer_class=FollowerSerializer)
    def friends(self, request, *args, **kwargs):
        self.queryset = UserLike.objects.select_related('to_user').filter(from_user=request.user, is_friend=True)
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['GET'], detail=True)
    def info(self, request, *args, **kwargs):
        from_user = request.user
        to_user = self.get_object()
        return Response(data={
            'user': UserSerializer(self.get_object()).data,
            'is_follow': UserLike.is_follow(from_user, to_user),
            'is_block': UserBlock.is_block(from_user, to_user)
        })

    @action(methods=['GET'], detail=False, serializer_class=BlockSerializer)
    def block_list(self, request, *args, **kwargs):
        self.queryset = UserBlock.objects.select_related('to_user').filter(from_user=request.user)
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['POST'], detail=True)
    def block(self, request, *args, **kwargs):
        UserBlock.block(request.user, self.get_object())
        return Response()

    @action(methods=['POST'], detail=True)
    def cancel_block(self, request, *args, **kwargs):
        UserBlock.cancel_block(request.user, self.get_object())
        return Response()

    @action(methods=['GET', 'PUT'], detail=False, serializer_class=UserMsgSettingSerializer)
    def msg_settings(self, request, *args, **kwargs):
        setting, created = UserMsgSetting.objects.get_or_create(user=request.user)
        if request.method == 'GET':
            serializer = UserMsgSettingSerializer(setting)
        else:
            serializer = UserMsgSettingSerializer(setting, data=request.data)
            if serializer.is_valid(raise_exception=True):
                serializer.save()
        return Response(data=serializer.data)

    @action(methods=['GET'], detail=False, serializer_class=VisitorSerializer)
    def visitor_list(self, request, *args, **kwargs):
        self.pagination_ordering = '-updated_at'
        self.queryset = UserVisit.objects.select_related('from_user').filter(to_user=request.user)
        if not request.GET.get('next'):
            read, _ = NotificationRead.objects.get_or_create(user=request.user)
            read.new_visitor = int(datetime.datetime.now().timestamp())
            read.save(update_fields=['new_visitor'])
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['GET'], detail=False, serializer_class=PostLikeSerializer)
    def like_post_list(self, request, *args, **kwargs):
        self.queryset = PostLike.objects.select_related('post', 'post__user').filter(user_id=request.user.id)
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['GET', 'PUT'], detail=False, serializer_class=AVCallSettingSerializer)
    def av_call_settings(self, request, *args, **kwargs):
        user = request.user
        setting, created = AVCallSetting.objects.get_or_create(user=user)
        if request.method == 'GET':
            serializer = AVCallSettingSerializer(setting, context={'request': request})
        else:
            serializer = AVCallSettingSerializer(setting, data=request.data, context={'request': request})
            if serializer.is_valid(raise_exception=True):
                with transaction.atomic():
                    serializer.save()
        return Response(data=serializer.data)

    @action(methods=['GET'], detail=True, serializer_class=PostSerializer)
    def post_list(self, request, *args, **kwargs):
        self.queryset = Post.objects.select_related('user').filter(user=self.get_object(), is_active=True)
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['GET'], detail=False, serializer_class=UserLevelSerializer)
    def level(self, request, *args, **kwargs):
        user_level, _ = UserLevel.objects.get_or_create(user=request.user)
        return Response(data=UserLevelSerializer(user_level).data)

    @action(methods=['POST'], detail=True)
    def chat_up(self, request, *args, **kwargs):
        with transaction.atomic():
            from_user = request.user
            to_user = self.get_object()
            ChatUpCount.check_count(from_user)
            UserChatUp.objects.create(from_user=from_user, to_user=to_user)
            gift = Gift.objects.filter(type=3).first()
            gift.send(from_user, to_user)
        # 发送搭讪礼物
        return Response()

    @action(methods=['POST'], detail=False)
    def face_auth(self, request, *args, **kwargs):
        user = request.user
        if not user.avatar or not user.avatar_real:
            raise CodeException(msg='请上传真实用户头像')
        if user.avatar_auth:
            raise CodeException(msg='您已完成真人认证')
        meta_info = request.data.get('meta_info')
        cert_id = InitFaceVerify.request_cert_id(request.user.id, str(uuid.uuid4()), meta_info)
        if not cert_id:
            raise CodeException(msg='发起人脸认证失败，请联系客服')
        user_auth, _ = UserAuth.objects.get_or_create(user=request.user)
        user_auth.cert_id = cert_id
        user_auth.save(update_fields=['cert_id'])
        return Response(data={'cert_id': cert_id})

    @action(methods=['POST'], detail=False)
    def name_auth(self, request, *args, **kwargs):
        name_auth, _ = NameAuth.objects.get_or_create(user=request.user)
        if name_auth.status == 1:
            raise CodeException(msg='您已完成实人认证')
        meta_info = request.data.get('meta_info')
        cert_name = request.data.get('cert_name')
        cert_no = request.data.get('cert_no')
        cert_id = InitFaceVerify.request_cert_id(request.user.id, str(uuid.uuid4()), meta_info, cert_no=cert_no,
                                                 cert_name=cert_name, product_code='ID_PRO')
        if not cert_id:
            raise CodeException(msg='发起人脸认证失败，请联系客服')
        name_auth.cert_id = cert_id
        name_auth.cert_name = cert_name
        name_auth.cert_no = cert_no
        name_auth.save(update_fields=['cert_id', 'cert_no', 'cert_name'])
        return Response(data={'cert_id': cert_id})

    @action(methods=['POST'], detail=False)
    def face_verify(self, request, *args, **kwargs):
        user = request.user
        res = DescribeFaceVerify.request_describe_face_verify(request.data.get('cert_id'))
        if not res:
            raise CodeException(msg='认证失败')
        if res.status_code == 200:
            body = res.body
            if body.code == '200':
                result = body.result_object
                material_info = json.loads(result.material_info)
                face_info = material_info.get('facialPictureFront')
                quality_score = float(face_info['qualityScore'])
                user_auth = UserAuth.objects.get(user=user)
                user_auth.score = quality_score
                if quality_score > 70:
                    user_auth.oss_bucket_name = face_info['ossBucketName']
                    user_auth.oss_object_name = face_info['ossObjectName']
                    user_auth.save(update_fields=['oss_bucket_name', 'oss_object_name', 'score'])
                    # 图片比对
                    target_url = face_info['pictureUrl']
                    res = CompareFaceVerify.compare(user.avatar.url, target_url)
                    if not res:
                        user.update_avatar_auth(False)
                        raise CodeException(msg='认证失败，请重新认证')
                    else:
                        if res.status_code == 200:
                            body = res.body
                            if body.code == '200' and float(body.result_object.verify_score) > 60:
                                user.update_avatar_auth(True)
                            else:
                                user.update_avatar_auth(False)
                                raise CodeException(msg='认证失败，认证头像与用户头像不是同一人')
                        else:
                            user.update_avatar_auth(False)
                            raise CodeException(msg='认证失败，请重新认证')

                else:
                    user_auth.save(update_fields=['score'])
                    raise CodeException(msg='认证失败，请重新认证')
            else:
                raise CodeException(msg=body.message)
        else:
            raise CodeException(msg='认证失败')
        return Response()

    @action(methods=['POST'], detail=False)
    def name_verify(self, request, *args, **kwargs):
        res = DescribeFaceVerify.request_describe_face_verify(request.data.get('cert_id'))
        if not res:
            raise CodeException('认证失败，请重新认证')
        result_obj = res.body.result_object
        name_auth = NameAuth.objects.get(user=request.user)
        material_info = json.loads(result_obj.material_info)
        face_info = material_info.get('facialPictureFront')
        verify_score = float(face_info['verifyScore'])
        name_auth.score = verify_score
        if verify_score > 70:
            name_auth.oss_bucket_name = face_info['ossBucketName']
            name_auth.oss_object_name = face_info['ossObjectName']
            name_auth.status = 1
            name_auth.save(update_fields=['status', 'oss_bucket_name', 'oss_object_name', 'score'])
        else:
            name_auth.status = 2
            name_auth.save(update_fields=['status', 'score'])
        return Response()

    @action(methods=['GET'], detail=True, serializer_class=GiftWallSerializer)
    def receive_gift_list(self, request, *args, **kwargs):
        self.queryset = GiftWall.objects.filter(gift__type__in=Gift.ACTIVE_GIFT).select_related('gift').filter(
            user=self.get_object())
        return super(UserViewSet, self).list(request, *args, **kwargs)

    @action(methods=['GET'], detail=True, serializer_class=GiftWallSerializer)
    def gift_wall(self, request, *args, **kwargs):
        gift_wall_list = GiftWall.objects.select_related('gift').filter(user=self.get_object(),
                                                                        gift__type__in=Gift.ACTIVE_GIFT)
        ids = [x.gift_id for x in gift_wall_list]
        gift_list = Gift.objects.filter(~Q(id__in=ids), type__in=Gift.ACTIVE_GIFT)
        _list = []
        for x in gift_wall_list:
            _list.append(GiftWallSerializer(x).data)
        for x in gift_list:
            _list.append({
                'gift': GiftSerializer(x).data,
                'count': 0
            })
        return Response(data={
            'total_count': len(gift_wall_list) + len(gift_list),
            'count': len(gift_wall_list),
            'list': _list
        })
